Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-29757: don't swallows errors in the socket.create_connection() utility loop #562

Closed
wants to merge 2 commits into from

Conversation

ankostis
Copy link

@ankostis ankostis commented Mar 8, 2017

Context

The utility method socket.create_connection() currently works like that:

  1. resolve the destination-address into one or more IP(v4 & v6) addresses;
  2. loop on each IP address and stop to the 1st one to work;
  3. if none works, re-raise the last error.

The problem

So currently the loop in socket.create_connection() ignores all intermediate errors and reports only the last connection failure, which might be irrelevant. For instance, when both IPv4 & IPv6 networks are supported, usually the last address is a IPv6 address and it frequently fails with an irrelevant error - the actual cause have already been ignored.

Possible solutions

To facilitate network debugging, there are at least 3 options:

a. log each failure as it happens, but that would get the final failure twice: once as a (warning?) message, and once as an exception .
b. collect all failures and log them only when connection fails, but that might miss important infos to the user;
c. collect and return all failures in list attached to the raised exception, which also might miss important infos.

open questions

Q1. A question for all cases is what logging "means" to use: the warnings or logging module?
Q2. And if logging is chosen, log them in 'DEBUG' or 'WARNING' level?
Q3. Finally, cases (b) & (c) need an answer whether to log collected errors in the case that connection passes.

The 1st part of case (c) is implemented in this PR.

@the-knights-who-say-ni
Copy link

Hello, and thanks for your contribution!

I'm a bot set up to make sure that the project can legally accept your contribution by verifying you have signed the PSF contributor agreement (CLA).

Unfortunately we couldn't find an account corresponding to your GitHub username on bugs.python.org (b.p.o) to verify you have signed the CLA. This is necessary for legal reasons before we can look at your contribution. Please follow these steps to help rectify the issue:

  1. If you don't have an account on b.p.o, please create one
  2. Make sure your GitHub username is listed in "Your Details" at b.p.o
  3. If you have not already done so, please sign the PSF contributor agreement. The "bugs.python.org username " requested by the form is the "Login name" field under "Your Details".
  4. If you just signed the CLA, please wait at least one US business day and then check "Your Details" on bugs.python.org to see if your account has been marked as having signed the CLA (the delay is due to a person having to manually check your signed CLA)
  5. Reply here saying you have completed the above steps

Thanks again to your contribution and we look forward to looking at it!

@mention-bot
Copy link

@ankostis, thanks for your PR! By analyzing the history of the files in this pull request, we identified @freddrake, @ethanfurman, @asvetlov, @giampaolo and @cf-natali to be potential reviewers.

@brettcannon
Copy link
Member

Could you open an issue to discuss this, @ankostis ? We prefer to discuss design and such on bugs.python.org so that GitHub is only used for code review.

@brettcannon brettcannon added the type-feature A feature request or enhancement label Mar 8, 2017
@ankostis
Copy link
Author

ankostis commented Mar 8, 2017

Issue 29757.

@ankostis
Copy link
Author

ankostis commented Mar 8, 2017

By the way my CLA has been signed.

@brettcannon brettcannon changed the title feat(socket): don't swallows errors in socket.create_connection() utility loop bpo-29757: don't swallows errors in the socket.create_connection() utility loop Mar 8, 2017
@brettcannon
Copy link
Member

brettcannon commented Mar 8, 2017

Thanks. And just in case you didn't notice, Travis failed (not sure if it's because of the PR changes).

@ankostis
Copy link
Author

ankostis commented Mar 8, 2017

Thanks, I will look at it tomorrow.

@brettcannon
Copy link
Member

@ankostis you can also wait for any discussion to be had about the issue before you put in more time in case this isn't accepted for some reason.

@ankostis
Copy link
Author

ankostis commented Mar 8, 2017

@brettcannon grateful for your care to contributors like me.

Anyhow, I did some changes on the OP regarding what I tried to implement and what I consider as possible solutions & open questions.

@vstinner vstinner requested review from vstinner and 1st1 March 9, 2017 17:15
Lib/socket.py Outdated
@@ -714,12 +714,12 @@ def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
return sock

except error as _:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't use _, it would look much better with a normal variable name such as e.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't use one letter name like "e" :-) Use "error", "exc" or whatever else.

Copy link
Author

@ankostis ankostis Mar 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in ec887c0c3 (rebased on latest master).
Had to rewrite again (b39d4b1) to rename e --> ex (i like it also this way, just trying to preserve styles).

Lib/socket.py Outdated
@@ -700,7 +700,7 @@ def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
"""

host, port = address
err = None
errs = []
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

errs -> errors

Copy link
Author

@ankostis ankostis Mar 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in ec887c0c3 b39d4b1 (rebased on latest master).

@1st1
Copy link
Member

1st1 commented Mar 9, 2017

I'll post some feedback to bpo.

## Context
The utility method `socket.create_connection()` currently works like that:
1. resolve the destination-address into one or more IP(v4 & v6) addresses;
2. loop on each IP address and stop to the 1st one to work;
3. if none works, re-raise the last error.

## The problem
So currently the loop in `socket.create_connection()` ignores all intermediate errors and reports only the last connection failure, 
which  might be irrelevant.  For instance, when both IPv4 & IPv6 networks are supported, usually the last address is a IPv6 address and it frequently fails with an irrelevant error - the actual cause have already been ignored.

## Possible solutions & open questions
To facilitate network debugging, there are at least 3 options:
a. log each failure [as they happen](/python/cpython/blob/6f0eb93183519024cb360162bdd81b9faec97ba6/Lib/socket.py#L717), but that would get the final failure twice: once as a (warning?) message, and once as an exception .
b. collect all failures and log them only when connection fails to collect the errors, 
   but that might miss important infos to the user;
c. collect and return all failures in list attached to the raised exception.

A question for cases (a) & (b) is what logging "means" to use: the `warnings` or `logging` module?
And if `logging` is chosen, log them in `'DEBUG'` or `'WARNING'` level?

Case (c), implemented in this PR sidesteps the above questions.
@ankostis
Copy link
Author

ankostis commented Mar 17, 2017

And just in case you didn't notice, Travis failed (not sure if it's because of the PR changes).

Now all TCs pass, because I re-raise the exact original error in case only a single one was collected.

But still, this PR is needs more work because of 2 issues:

Abuse of OSError() constructor

When more than one error is collected, I pass a string to the 1st arg to OSError() constructor (instead of an errno).
But I copied this from existing code.

Which errno should use there?

Notify user about collected errors on Success

The list of intermediate errors is lost in the case of success.
As suggested on the OP, this may happen can be corrected (in my order of preference):

  1. with a new optional argument for the user to provide the list to collect the errors (it changes the API backward-compatibly but allows inspection of the errors);
  2. with logging logs (more configurable than warning, but no inspection - only strings);
  3. with warnings;
  4. by doing nothing (swallowing error-messages is dangerous in the long term).

@ankostis
Copy link
Author

@the-knights-who-say-ni I have signed CLA; is there some problem with it?

- Rename `_` and `errs` variable names.
- Raise original error if a single one.
- Improve a bit error message, but still no Errno provided for when
multiple errors are included.
@brettcannon
Copy link
Member

@ankostis I just checked the logs and it seems you didn't set your GitHub username on your bugs.python.org (i.e. the bot can't find any account on bugs.python.org that has the GitHub username "ankostis" set). Fix that and hopefully the bot will pick up that you signed the CLA.

@ankostis
Copy link
Author

ankostis commented Mar 17, 2017

OK, my gh-username has been set.

@brettcannon
Copy link
Member

@ankostis that worked!

@ankostis
Copy link
Author

ankostis commented Mar 22, 2017

Any ideas which road to take?

And where should the discussion take place, here on on python-tracker?

@brettcannon
Copy link
Member

@ankostis code review issues are discussed here, all other discussions (such as what the best approach is, etc.) should happen on the issue.

Copy link
Contributor

@MaxwellDupre MaxwellDupre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that PEP 654 -- Exception Groups and except*
would be the best way to implement multiple exceptions here, but this will need to wait while being implemented. Or you could try the without except*
https://www.python.org/dev/peps/pep-0654/#programming-without-except

@github-actions
Copy link

This PR is stale because it has been open for 30 days with no activity.

@github-actions github-actions bot added the stale Stale PR or inactive for long period of time. label Mar 10, 2022
@kumaraditya303
Copy link
Contributor

Superseded by #91586

jaraco pushed a commit that referenced this pull request Dec 2, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
awaiting review stale Stale PR or inactive for long period of time. type-feature A feature request or enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants